package com.bookmngsys.service.impl;

import com.bookmngsys.dao.*;
import com.bookmngsys.entity.*;
import com.bookmngsys.service.BookInfoService;
import com.bookmngsys.service.BookService;
import com.bookmngsys.service.ReservationService;
import com.bookmngsys.utils.CommonUtils;
import com.bookmngsys.utils.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpSession;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author han
 */
@Service
public class BookServiceImpl implements BookService {
    @Autowired
    ReaderDao readerDao;
    @Autowired
    BookDao bookDao;
    @Autowired
    BorrowBookDao borrowBookDao;
    @Autowired
    CategoryDao categoryDao;
    @Autowired
    ReservationDao reservationDao;
    @Autowired
    ReservationService reservationService;
    @Autowired
    BookInfoDao bookInfoDao;
    @Autowired
    BookInfoService bookInfoService;

    @Autowired
    BorrowHistoryDao borrowHistoryDao;
    @Autowired
    ReservationHistoryDao reservationHistoryDao;
    @Autowired
    FineHistoryDao fineHistoryDao;
    @Autowired
    BookLocationDao bookLocationDao;

    @Override
    public List<Book> getBooks(int curPage, int pageSize, boolean isAdmin) {
        return bookDao.findEnabledBooks(curPage, pageSize);
    }

    @Override
    public List<Book> searchByBookName(String bookName){
        return bookDao.searchByBookName(bookName);
    }

    @Override
    public List<Book> searchByAuthor(String author){
        return bookDao.searchByAuthor(author);
    }

    @Override
    public List<Book> searchByIsbn(String isbn){
        return bookDao.searchByIsbn(isbn);
    }

    @Override
    public Book getBook(String isbn) {
        return bookDao.findById(isbn).orElse(null);
    }

    private void isBookValid(Book book) throws Exception {
        if(book == null || book.getStatus() == 0){
            throw new Exception("book is forbid or not exist");
        }
    }

    /**
     * 预约人借书
     * @param isbn
     * @param readerId
     * @throws Exception
     */
    @Transactional
    @Override
    public void borrowBookByReserve(String isbn, int readerId) throws Exception {
        isBookValid(bookDao.findByIsbn(isbn));
        BookInfo target = bookInfoDao.findReservationByIsbnAndReaderId(isbn, readerId, System.currentTimeMillis());
        if(target == null){
            throw new Exception("can not find reservation or reservation is overdue");
        }else if(System.currentTimeMillis() > target.getReturnDate()){
            throw new Exception("you have overdue");
        }
        bookDao.reduceBookNum(isbn);
        target.setBorrowReaderId(readerId);
        target.setBorrowDate(System.currentTimeMillis());
        target.setReturnDate(target.getBorrowDate() + 10 * 24 * 60 * 60 * 1000);
        target.setReserveReaderId(null);
        target.setReserveDate(null);
        target.setFine(0.0f);
        bookInfoDao.save(target);
    }

    /**
     * 续订
     * @param isbn
     * @param readerId
     * @throws Exception
     */
    @Transactional
    @Override
    public void renew(String isbn, int readerId) throws Exception {
        BookInfo bookInfo = bookInfoDao.findBorrowBookByIsbnAndReaderId(isbn, readerId);
        if (bookInfo == null) {
            throw new Exception("do not borrow");
        } else if (bookInfo.getReturnDate() >= System.currentTimeMillis()) {
            //可续约
            long returnDate = bookInfo.getReturnDate();
            bookInfo.setReturnDate(System.currentTimeMillis() + 10 * 24 * 3600 * 1000);
            bookInfoDao.save(bookInfo);
            {
                //同步更新borrow_history表
                BorrowHistory a = borrowHistoryDao.findValidBorrowHistory(bookInfo.getIsbn(),
                        bookInfo.getBookId(),
                        bookInfo.getBorrowReaderId(),
                        bookInfo.getBorrowDate(),
                        returnDate);
                if(a != null){
                    a.setReturnDate(bookInfo.getReturnDate());
                    borrowHistoryDao.save(a);
                }
            }
        }else{
            throw new Exception("please pay fine");
        }
    }

    @Override
    public void deleteBookByIsbn(String isbn) throws Exception {
        Book book = this.getBook(isbn);
        if(book==null){
            throw new Exception("no book");
        }
        else if(book.getAllNum()!=0||book.getLastNum()!=0){
            throw new Exception("cannot delete by isbn");
        }else{
            bookDao.delete(book);
        }
    }

    @Override
    public void deleteBookByBookId(int bookid) throws Exception {
        BookInfo bookByBookId = bookInfoDao.findBookByBookId(bookid);
        if(bookByBookId == null){
            throw new Exception("no book");
        }
        //如果有借出，不能删
        else if(bookByBookId.getReserveReaderId() != null){
            throw new Exception("book was reserved");
        }else if(bookByBookId.getBorrowReaderId() != null){
            throw new Exception("book was borrowed");
        }else{
            bookInfoDao.delete(bookByBookId);
            Book book =  this.getBook(bookByBookId.getIsbn());
            book.setAllNum(book.getAllNum()-1);
            book.setLastNum(book.getLastNum()-1);
            this.addBook(book);
        }


    }

    @Override
    public void addCategory(String category) {
        Category categoryTemp = new Category();
        categoryTemp.setCategoryName(category);
        categoryDao.save(categoryTemp);
    }

    @Override
    public void deleteCategory(int id) throws Exception {
        List<Book> bookDaoAll = bookDao.findAll();
        for(Book book : bookDaoAll){
            if(book.getCategory().getId() == id){
                throw new Exception("cannot delete(used)");
            }
        }
        categoryDao.deleteById(id);
    }

    @Override
    public List<BookLocation> getBookLocation() {
        return bookLocationDao.findAll();
    }

    @Override
    public void addBookLocation(BookLocation bookLocation) throws Exception {
        if(bookLocation.getRackNumber()==null){
            throw new Exception("racknumber cannot be null");
        }
        List<BookLocation> list = bookLocationDao.findAll();
        for(BookLocation loc : list){
            if(loc.getRackNumber() == bookLocation.getRackNumber()){
                throw new Exception("location is already created");
            }
        }
        bookLocationDao.save(bookLocation);
    }

    @Override
    public void deleteBookLocation(String rackNumber) throws Exception {

        BookLocation bookLocation = new BookLocation();
        bookLocation.setRackNumber(rackNumber);
        List<Book> bookDaoAll = bookDao.findAll();
        for(Book book : bookDaoAll){
            if(book.getRackNumber().equals(rackNumber)){
                throw new Exception("cannot delete(used)");
            }
        }
        bookLocationDao.delete(bookLocation);
    }

    @Override
    public int getNumOfOverdue(int readerId){
        ArrayList<BookInfo> bookInfos = bookInfoDao.getBorrowBookByReaderId(readerId);
        int overdue = 0;
        for(BookInfo bookInfo:bookInfos){
            if(System.currentTimeMillis() > bookInfo.getReturnDate()){
                overdue++;
            }
        }
        return overdue;
    }
    /**
     * 非预约人借书
     * @param bookId
     * @param readerId
     * @throws Exception
     */
    @Transactional
    @Override
    public void borrowBook(int bookId, int readerId) throws Exception {
        BookInfo bookInfo = bookInfoDao.findBookByBookId(bookId);
        isBookValid(bookDao.findByIsbn(bookInfo.getIsbn()));
        //bookInfoDao.findBorrowBookByIsbnAndReaderId(bookInfo.getIsbn(), readerId);
        //bookInfoDao.findReservationByIsbnAndReaderId(bookInfo.getIsbn(), readerId, System.currentTimeMillis());
        if(getNumOfOverdue(readerId) != 0){
            throw new Exception("you have book overdue, please pay fine");
        }else if(bookInfo == null){
            throw new Exception("can not find this book");
        }else if(bookInfo.getBorrowReaderId() != null){
            throw new Exception("have been borrowed");
        }else if((bookInfo.getReserveReaderId() != null && bookInfo.getReserveReaderId() != readerId)  && bookInfo.getReserveDate() != null && bookInfo.getReserveDate() + 14400 * 1000 >= System.currentTimeMillis()){
            throw new Exception("have been reserved");
        }else if(bookInfo.getDamage() == 0){
            throw new Exception("book is damaaged");
        }else if(bookInfo.getLost() == 0){
            throw new Exception("book is lost");
        }else{
            {
                //同步更新reservation_history
                ReservationHistory a = reservationHistoryDao.findValidReservation(bookInfo.getReserveReaderId(),
                        bookInfo.getIsbn(),
                        bookInfo.getBookId(),
                        bookInfo.getReserveDate());
                if(a != null){
                    a.setIsUsed(true);
                    reservationHistoryDao.save(a);
                }
            }

            bookDao.reduceBookNum(bookInfo.getIsbn());
            bookInfo.setBorrowReaderId(readerId);
            bookInfo.setBorrowDate(System.currentTimeMillis());
            bookInfo.setReturnDate(bookInfo.getBorrowDate() + 10 * 24 * 60 * 60 * 1000);
            bookInfo.setReserveReaderId(null);
            bookInfo.setReserveDate(null);
            bookInfo.setFine(0.0f);
            bookInfoDao.save(bookInfo);
            {
                //向borrow_history中同步插入🔚数据
                BorrowHistory borrowHistory = new BorrowHistory();
                borrowHistory.setId((new Random(System.currentTimeMillis())).nextInt(Integer.MAX_VALUE));
                borrowHistory.setBorrowDate(bookInfo.getBorrowDate());
                borrowHistory.setBookId(bookInfo.getBookId());
                borrowHistory.setIsbn(bookInfo.getIsbn());
                borrowHistory.setReturnDate(bookInfo.getReturnDate());
                borrowHistory.setReaderId(bookInfo.getBorrowReaderId());
                borrowHistory.setIsbn(bookInfo.getIsbn());
                borrowHistory.setIsReturned(false);
                borrowHistoryDao.save(borrowHistory);

            }
        }
    }

    ArrayList<BorrowBook> exchange(ArrayList<BookInfo> arr){
        ArrayList<BorrowBook> borrowBooks = new ArrayList<>();
        for(BookInfo bookInfo: arr){
            BorrowBook borrowBook = new BorrowBook();
            Book book = bookDao.findByIsbn(bookInfo.getIsbn());
            if(book == null){
                continue;
            }
            borrowBook.setBookId(bookInfo.getBookId());
            borrowBook.setBook(book);
            borrowBook.setReader(readerDao.searchByReaderId(bookInfo.getBorrowReaderId()));
            borrowBook.setBorrowDate(new Date(bookInfo.getBorrowDate()));
            borrowBook.setReturnDate(new Date(bookInfo.getReturnDate()));
            borrowBooks.add(borrowBook);
        }
        return borrowBooks;
    }
    void updateFine(BookInfo bookInfo){
        bookInfo.setFine(CommonUtils.getFineAmount(bookInfo.getReturnDate()));
    }
    void updateFine(ArrayList<BookInfo> bookInfos){
        for(BookInfo bookInfo:bookInfos){
            updateFine(bookInfo);
        }
    }
    @Override
    public ArrayList<BorrowBook> getBorrowBooks(int readerId) {
        ArrayList<BookInfo> bookInfos = bookInfoDao.findBorrowBookByReaderId(readerId);
        updateFine(bookInfos);
        ArrayList<BorrowBook> borrowBooks = exchange(bookInfos);

        SimpleDateFormat formatter= new SimpleDateFormat("yyyy-MM-dd");
        Date currentDate = new Date();
        return (ArrayList<BorrowBook>) borrowBooks.stream().map(borrowBook -> {
            borrowBook.setFine(CommonUtils.getFineAmount(borrowBook.getReturnDate().getTime()));
            return borrowBook;
        }).collect(Collectors.toList());
    }



    @Override
    public List<BorrowBook> getBorrowBooksByIsbn(String isbn) {
        ArrayList<BookInfo> bookInfos = bookInfoDao.findBorrowBookByIsbn(isbn);
        updateFine(bookInfos);
        ArrayList<BorrowBook> borrowBooks = exchange(bookInfos);

        SimpleDateFormat formatter= new SimpleDateFormat("yyyy-MM-dd");
        Date currentDate = new Date();
        return (ArrayList<BorrowBook>) borrowBooks.stream().map(borrowBook -> {
            borrowBook.setFine(CommonUtils.getFineAmount(borrowBook.getReturnDate().getTime()));
            return borrowBook;
        }).collect(Collectors.toList());

    }

    @Override
    @Transactional
    public void payMoney(int bookId, String outTradeNo) throws Exception {
        BookInfo bookInfo = bookInfoDao.findBorrowBookByBookId(bookId);
        if(bookInfo == null){
            throw new Exception("can not find information");
        }else if(CommonUtils.getBorrowReaderId(bookInfo) == null || CommonUtils.getFineAmount(bookInfo.getReturnDate()) == 0.0){
            //该书没有人借或没欠款
            return;
        }
        long newReturnDate = System.currentTimeMillis() + 3600 * 1000;
        {
            //同步更新borrow_history表
            BorrowHistory a = borrowHistoryDao.findValidBorrowHistory(bookInfo.getIsbn(),
                    bookInfo.getBookId(),
                    bookInfo.getBorrowReaderId(),
                    bookInfo.getBorrowDate(),
                    bookInfo.getReturnDate());
            if(a != null){
                a.setReturnDate(newReturnDate);
                borrowHistoryDao.save(a);
            }
        }
        {
            //付款账单同步
            FineHistory a = new FineHistory();
            a.setIsbn(bookInfo.getIsbn());
            a.setReaderId(bookInfo.getBorrowReaderId());
            a.setBookId(bookInfo.getBookId());
            a.setId((new Random(System.currentTimeMillis())).nextInt(Integer.MAX_VALUE));
            a.setDiscription("overdue payment");
            a.setFineAmount(CommonUtils.getFineAmount(bookInfo.getReturnDate()));
            a.setPayFineDate(System.currentTimeMillis());
            a.setOutTradeNo(outTradeNo);
            fineHistoryDao.save(a);
        }
        bookInfo.setFine((float)0);
        bookInfo.setReturnDate(newReturnDate);
        bookInfoDao.save(bookInfo);
    }

    @Override
    @Transactional
    public void returnBook(int bookId) throws Exception {
        BookInfo bookInfo = bookInfoDao.findBorrowBookByBookId(bookId);
        if(bookInfo == null){
            throw new Exception("can not find borrow information");
        }else{
            if(bookInfo.getReturnDate() < System.currentTimeMillis()){
                throw new Exception("you have money to pay");
            }
            {
                //同步更新borrow_history表
                BorrowHistory a = borrowHistoryDao.findValidBorrowHistory(bookInfo.getIsbn(),
                        bookInfo.getBookId(),
                        bookInfo.getBorrowReaderId(),
                        bookInfo.getBorrowDate(),
                        bookInfo.getReturnDate());
                if(a != null){
                    a.setIsReturned(true);
                    a.setRealReturnDate(System.currentTimeMillis());
                    borrowHistoryDao.save(a);
                }
            }
            bookDao.addBookNum(bookInfo.getIsbn());
            bookInfo.setReserveDate(null);
            bookInfo.setReserveReaderId(null);
            bookInfo.setBorrowReaderId(null);
            bookInfo.setReturnDate(null);
            bookInfo.setBorrowDate(null);
            bookInfo.setFine(0.0f);
            bookInfoDao.save(bookInfo);
        }

    }

    @Override
    public void enableBook(String isbn) {
        bookDao.updateBookStatus(isbn, 1);
    }

    @Override
    public void disableBook(String isbn) {
        bookDao.updateBookStatus(isbn, 0);
    }

    @Override
    public List<Category> getCategory() {
        return categoryDao.findAll();
    }

    @Override
    public void addBook(Book book) {
        bookDao.save(book);
    }

    @Override
    public void updateBook(String originBook, Book book, HttpSession httpsession) throws Exception {
        Book originalBook = this.getBook(originBook);
        if(originalBook.getAllNum() > book.getAllNum()){
            throw new Exception("cannot delete book");
        }
        else{
            int num = book.getAllNum() - originalBook.getAllNum();
            System.out.println("ori:" + originalBook.getAllNum());
            System.out.println("new:" + book.getAllNum());
            if(num > 0){
                for(int i=0;i<num;i++){
                    bookInfoService.addBookToBookInfo(book.getIsbn(),i+1,httpsession);
                }
                book.setLastNum(originalBook.getLastNum()+num);
            }
        }
        bookDao.save(book);
    }

    @Override
    public void updateImg(String isbn, String Url) {
        bookDao.updateBookImg(isbn,Url);
    }
}
